package ecs.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class ByteBuffer {

    private byte[] _buffer;
    private int _readIndex;
    private int _writeIndex;
    private int _offset;
    private int _length;

    public ByteBuffer(int capacity) {
        _buffer = new byte[capacity];
        _offset = 0;
        _length = capacity;
        _readIndex = _offset;
        _writeIndex = _offset;
    }

    public ByteBuffer(byte[] buffer) {
        _buffer = buffer;
        _offset = 0;
        _length = buffer.length;
        _readIndex = _offset;
        _writeIndex = _offset;
    }

    public ByteBuffer(byte[] buffer, int offset, int length) {
        _buffer = buffer;
        _offset = offset;
        _length = length;
        _readIndex = _offset;
        _writeIndex = _offset;
    }

    public int readRemaining() {
        int remaining = _writeIndex - _readIndex;
        return Math.max(remaining, 0);
    }

    private int remaining() {
        int remaining = _length + _offset - _writeIndex;
        return Math.max(remaining, 0);
    }

    public int writeRemaining() {
        int remaining = _length - readRemaining();
        return Math.max(remaining, 0);
    }

    public static int copy(ByteBuffer src, ByteBuffer dist) {
        int copyCount;
        if (src.readRemaining() <= dist.remaining()) {
            copyCount = dist.remaining();
        } else if (src.readRemaining() <= dist.writeRemaining()) {
            dist.compact();
            copyCount = src.readRemaining();
        } else {
            if (dist.readRemaining() != dist.writeRemaining())
                dist.compact();
            copyCount = dist.writeRemaining();
        }
        System.arraycopy(src._buffer, src._readIndex, dist._buffer, dist._readIndex, copyCount);
        src._readIndex += copyCount;
        dist._writeIndex += copyCount;
        return copyCount;
    }

    public int write(InputStream in) throws IOException {
        if (writeRemaining() <= 0) {
            return 0;
        }
        compact();
        return in.read(_buffer, _writeIndex, writeRemaining());
    }

    public int read(OutputStream out) throws IOException {
        if (readRemaining() <= 0) {
            return 0;
        }
        int readCount = readRemaining();
        out.write(_buffer, _readIndex, readCount);
        _readIndex += readCount;
        return readCount;
    }

    public ByteBuffer writeInt(int value) {
        if (writeRemaining() < 4) {
            throw new RuntimeException("write remaining less 4!");
        }
        _buffer[_writeIndex++] = (byte) (value >> 24);
        _buffer[_writeIndex++] = (byte) (value >> 16);
        _buffer[_writeIndex++] = (byte) (value >> 8);
        _buffer[_writeIndex++] = (byte) (value);
        return this;
    }

    public int readInt() {
        if (_writeIndex - _readIndex < 4) {
            throw new RuntimeException("read remaining less 4!");
        }
        return (_buffer[_readIndex++] & (0xFF)) << 24
                | (_buffer[(_readIndex++)] & (0xFF)) << 16
                | (_buffer[(_readIndex++)] & (0xFF)) << 8
                | (_buffer[(_readIndex++)] & (0xFF));
    }

    public void clear() {
        _readIndex = _offset;
        _writeIndex = _offset;
    }

    private void compact() {
        int len = readRemaining();
        System.arraycopy(_buffer, _readIndex, _buffer, _offset, len);
        _readIndex = _offset;
        _writeIndex = _readIndex + len;
    }
}
